package com.zaizai.config;

import org.apache.ibatis.io.VFS;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

/**
 * Mybatis支持*匹配扫描包
 */
@Configuration
public class MyBatisConfig {
    @Autowired
    private Environment env;

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    /**
     * 动态设置typeAliasesPackage
     *
     * @param typeAliasesPackage 以逗号分隔的包路径
     * @return 经处理后的包路径
     */
    /**
     * 设置类型别名包
     * 该方法用于扫描指定包路径下的所有类，并将其包名作为类型别名返回
     * 主要用于MyBatis配置，以避免手动重复配置相同的包名
     *
     * @param typeAliasesPackage 以逗号分隔的包路径字符串，表示需要扫描的包
     * @return 返回拼接后的包名字符串，用于MyBatis类型别名配置
     * @throws RuntimeException 如果扫描路径下未找到任何包，抛出运行时异常
     */
    public static String setTypeAliasesPackage(String typeAliasesPackage) {
        // 初始化资源模式解析器和元数据读取工厂
        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
        List<String> allResult = new ArrayList<String>();
        try {
            // 分割包路径字符串，逐个处理每个包路径
            for (String aliasesPackage : typeAliasesPackage.split(",")) {
                List<String> result = new ArrayList<String>();
                // 将包路径转换为资源路径，并添加默认的资源模式
                aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN;
                // 获取资源数组
                Resource[] resources = resolver.getResources(aliasesPackage);
                if (resources != null && resources.length > 0) {
                    MetadataReader metadataReader = null;
                    // 遍历资源数组，读取每个资源的元数据
                    for (Resource resource : resources) {
                        if (resource.isReadable()) {
                            metadataReader = metadataReaderFactory.getMetadataReader(resource);
                            try {
                                // 加载类并获取包名，添加到结果列表中
                                result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
                            } catch (ClassNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                // 使用HashSet去除重复的包名，并合并到最终结果列表中
                if (result.size() > 0) {
                    HashSet<String> hashResult = new HashSet<String>(result);
                    allResult.addAll(hashResult);
                }
            }
            // 根据最终结果列表的情况，返回拼接后的包名字符串或抛出异常
            if (allResult.size() > 0) {
                typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0]));
            } else {
                throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return typeAliasesPackage;
    }

    /**
     * 解析mapper文件位置
     *
     * @param mapperLocations mapper文件的位置数组
     * @return 解析后的资源数组
     */
    /**
     * 根据提供的映射位置数组，解析并返回对应的资源数组
     * 该方法主要用于加载映射文件，如MyBatis的Mapper XML文件
     *
     * @param mapperLocations 映射文件的位置数组，表示要解析的资源路径
     * @return Resource[] 解析后的资源数组
     */
    public Resource[] resolveMapperLocations(String[] mapperLocations) {
        // 创建资源模式解析器，用于解析资源路径
        ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
        // 初始化资源列表，用于存储解析后的资源
        List<Resource> resources = new ArrayList<Resource>();

        // 检查映射位置数组是否非空
        if (mapperLocations != null) {
            // 遍历映射位置数组
            for (String mapperLocation : mapperLocations) {
                try {
                    // 解析每个映射位置的资源，并添加到资源列表中
                    Resource[] mappers = resourceResolver.getResources(mapperLocation);
                    resources.addAll(Arrays.asList(mappers));
                } catch (IOException e) {
                    // 忽略解析过程中出现的IO异常
                }
            }
        }

        // 将资源列表转换为资源数组并返回
        return resources.toArray(new Resource[resources.size()]);
    }

    /**
     * 创建SqlSessionFactory对象
     *
     * @param dataSource 数据源
     * @return SqlSessionFactory对象
     * @throws Exception 如果创建过程中出现错误则抛出异常
     */
    @Bean
    /**
     * 创建SqlSessionFactory实例
     *
     * @param dataSource 数据源，用于数据库连接
     * @return SqlSessionFactory 实例，用于生成SqlSession
     * @throws Exception 当创建过程发生错误时抛出异常
     */
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // 从配置文件中获取MyBatis的类型别名包
        String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage");
        // 从配置文件中获取MyBatis的映射文件位置
        String mapperLocations = env.getProperty("mybatis.mapperLocations");
        // 从配置文件中获取MyBatis的配置文件位置
        String configLocation = env.getProperty("mybatis.configLocation");

        // 设置类型别名包，如果未配置则使用默认值
        typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage);
        // 添加SpringBootVFS实现类，用于处理资源文件
        VFS.addImplClass(SpringBootVFS.class);

        // 创建SqlSessionFactoryBean实例
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        // 设置数据源
        sessionFactory.setDataSource(dataSource);
        // 设置类型别名包
        sessionFactory.setTypeAliasesPackage(typeAliasesPackage);
        // 设置映射文件位置
        if (StringUtils.hasText(mapperLocations)) {
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            sessionFactory.setMapperLocations(resolver.getResources(mapperLocations));
        } else {
            sessionFactory.setMapperLocations(new Resource[]{});
        }
        // 设置配置文件位置
        sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));
        // 返回SqlSessionFactory实例
        return sessionFactory.getObject();
    }
}
